using System;
using System.IO;
using BSDS.Core.Infrastructure.Logging;

namespace BSDS.Core.Infrastructure.TransferProgress
{
    internal class WriteProgressReportingStream : Stream
    {
        private const string LogTemplate = "progress {0}%";

        private readonly Logger logger;

        private readonly Stream internalStream;
        private readonly long totalSizeInBytes;
        
        private double lastReportedProgress;
        private long bytesWritten;

        public WriteProgressReportingStream(Stream internalStream, long totalSizeInBytes, Logger logger)
        {
            this.internalStream = internalStream;
            this.totalSizeInBytes = totalSizeInBytes;
            this.logger = logger;

            lastReportedProgress = 0.0;
            bytesWritten = 0;
        }

        public override void Flush()
        {
            internalStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return internalStream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            internalStream.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return internalStream.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            internalStream.Write(buffer, offset, count);

            bytesWritten += count;
            var currentProgress = ((double)bytesWritten) / totalSizeInBytes;

            if (Math.Abs(lastReportedProgress - currentProgress) > 0.1)
            {
                var progressPercentage = Math.Min(currentProgress * 100, 100).ToString("F");

                logger.Log(string.Format(LogTemplate, progressPercentage));

                lastReportedProgress = currentProgress;
            }
        }

        public override bool CanRead
        {
            get { return internalStream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return internalStream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return internalStream.CanWrite; }
        }

        public override long Length
        {
            get { return internalStream.Length; }
        }

        public override long Position
        {
            get { return internalStream.Position; }
            set { internalStream.Position = value; }
        }
    }
}